# -*- coding: utf-8 -*-
# @Time    : 2024/7/12 16:30
# @Author  : yujiahao
# @File    : 24_fastapi_request_put.py
# @description:请求体 - 更新数据
from http.client import HTTPException
from typing import List, Union

from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel

app = FastAPI()


# todo 用 PUT 更新数据

class Item(BaseModel):
    name: Union[str, None] = None
    description: Union[str, None] = None
    price: Union[float, None] = None
    tax: float = 10.5
    tags: List[str] = []


items = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
    "baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}


@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: str):
    return items[item_id]


# 把输入数据转换为以 JSON 格式存储的数据（比如，使用 NoSQL 数据库时），可以使用 jsonable_encoder。例如，把 datetime 转换为 str
@app.put("/items/{item_id}", response_model=Item)
async def update_item(item_id: str, item: Item):
    # 使用 jsonable_encoder 将 Pydantic 模型对象转换为 JSON 兼容的数据类型
    update_item_encoded = jsonable_encoder(item)
    items[item_id] = update_item_encoded
    return update_item_encoded


# todo 用 PATCH 进行部分更新

'''
HTTP PATCH 操作用于更新 部分 数据。即，只发送要更新的数据，其余数据保持不变。

PATCH 没有 PUT 知名，也怎么不常用,很多人甚至只用 PUT 实现部分更新。FastAPI 对此没有任何限制，可以随意互换使用这两种操作。


更新部分数据时，可以在 Pydantic 模型的 .dict() 中使用 exclude_unset 参数。

比如，item.dict(exclude_unset=True)。

这段代码生成的 dict 只包含创建 item 模型时显式设置的数据，而不包括默认值。
然后再用它生成一个只含已设置（在请求中所发送）数据，且省略了默认值的 dict。



简而言之，更新部分数据应：

    - 使用 PATCH 而不是 PUT （可选，也可以用 PUT）；
    - 提取存储的数据；
    - 把数据放入 Pydantic 模型；
    - 生成不含输入模型默认值的 dict （使用 exclude_unset 参数）；
    - 只更新用户设置过的值，不用模型中的默认值覆盖已存储过的值。
    - 为已存储的模型创建副本，用接收的数据更新其属性 （使用 update 参数）。
    - 把模型副本转换为可存入数据库的形式（比如，使用 jsonable_encoder）。
    - 这种方式与 Pydantic 模型的 .dict() 方法类似，但能确保把值转换为适配 JSON 的数据类型，例如， 把 datetime 转换为 str 。
    - 把数据保存至数据库；
    - 返回更新后的模型。
'''


# 定义一个路径操作，使用 PATCH 方法部分更新指定 ID 的项目
# 一个是用exclude_unset更新部分数据，一个是调用update更新数据
@app.patch("/items_1/{item_id}", response_model=Item)
async def update_item_1(item_id: str, item: Item):
    """
    部分更新指定 ID 的项目数据，并将其存储在伪数据库中。

    参数:
    - item_id: 项目的 ID，类型为字符串
    - item: 包含要更新的数据的 Pydantic 模型对象

    返回:
    - 更新后的项目数据，类型为 Item
    """
    # 从伪数据库中获取现有的项目数据
    stored_item_data = items.get(item_id)

    if not stored_item_data:
        raise HTTPException(status_code=404, detail="Item not found")

    # 将现有的数据转换为 Pydantic 模型对象
    stored_item_model = Item(**stored_item_data)

    # 使用 exclude_unset 参数，将传入的数据转换为字典，只包含有值的字段
    update_data = item.dict(exclude_unset=True)

    # 创建一个新的 Pydantic 模型对象，更新现有模型中的数据
    updated_item = stored_item_model.copy(update=update_data)

    # 将更新后的项目数据转换为 JSON 兼容的数据类型，并存储在伪数据库中
    items[item_id] = jsonable_encoder(updated_item)

    # 返回更新后的项目数据
    return updated_item
